import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, HttpStatusCode, InternalAxiosRequestConfig } from 'axios'
import { R } from '@/components/axios/interface'
import { AxiosCanceler } from './helper/axiosCancel'
import UnauthorizedError from '@/core/framework/error/UnauthorizedError'
import ForbiddenError from '@/core/framework/error/ForbiddenError'
import * as qs from 'qs'
import globalConfig from '@/core/framework/config/globalConfig'
import { translate } from '@/core/i18n/service/i18nService'

const axiosCanceler = new AxiosCanceler()

const config = {
	// 默认地址请求地址，可在 .env 开头文件中修改
	baseURL: globalConfig.apiBaseUrl,
	// 设置超时时间（10s）
	timeout: 10000,
	// 跨域时候允许携带凭证
	withCredentials: true,
}

class RequestHttp {
	service: AxiosInstance

	public constructor(config: AxiosRequestConfig) {
		// 实例化axios
		this.service = axios.create(config)

		/**
		 * @description
		 * 1、添加取消拦截
		 * 2、添加智能格式化参数
		 */
		this.service.interceptors.request.use((config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => {
			// * 将当前请求添加到 pending 中
			axiosCanceler.addPending(config)
			// 智能格式化参数
			this.smartParams(config)

			return config
		})
	}

	// * 常用请求方法封装
	get<T>(requestConfig: AxiosRequestConfig): Promise<R<T>> {
		return this.requestInternal({ method: 'get', ...requestConfig }) as Promise<R<T>>
	}

	post<T>(requestConfig: AxiosRequestConfig): Promise<R<T>> {
		return this.requestInternal({ method: 'post', ...requestConfig }) as Promise<R<T>>
	}

	put<T>(requestConfig: AxiosRequestConfig): Promise<R<T>> {
		return this.requestInternal({ method: 'put', ...requestConfig }) as Promise<R<T>>
	}

	delete<T>(requestConfig: AxiosRequestConfig): Promise<R<T>> {
		return this.requestInternal({ method: 'delete', ...requestConfig }) as Promise<R<T>>
	}

	cancelRequest = (url: string | string[]) => {
		const urlList = Array.isArray(url) ? url : [url]
		for (const _url of urlList) {
			axiosCanceler.removePendingByUrl(_url)
		}
	}
	cancelAllRequest = () => {
		axiosCanceler.removeAllPending()
	}
	private requestInternal = async <T>(requestConfig: AxiosRequestConfig): Promise<R<T>> => {
		return this.service(requestConfig).then((response: AxiosResponse) => {
			if (response.status === HttpStatusCode.Forbidden) {
				throw new ForbiddenError(translate('security.forbidden', '权限不足,禁止访问'))
			}
			if (response.status === HttpStatusCode.Unauthorized) {
				throw new UnauthorizedError(translate('security.unauthorized', '登录已过期,请重新登录'))
			}
			if (response.data.hasOwnProperty('code')) {
				return response.data
			}
			throw new Error('服务端数据结构解析失败')
		})
	}

	private smartParams = (config: InternalAxiosRequestConfig) => {
		if (config.method === 'post' && config.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
			config.data = qs.stringify(config.data)
		} else if (config.method === 'post' && config.headers['Content-Type'] === 'multipart/form-data') {
			config.data = this.objToFormData(config.data)
		}

		if (config.method === 'get' && config.params) {
			let url = config.url as string
			url += '?'
			const keys = Object.keys(config.params)
			for (const key of keys) {
				if (config.params[key] !== void 0 && config.params[key] !== null) {
					url += `${key}=${encodeURIComponent(config.params[key])}&`
				}
			}
			url = url.substring(0, url.length - 1)
			config.params = {}
			config.url = url
		}
		return config
	}

	private objToFormData = (obj: Recordable) => {
		const formData = new FormData()
		Object.keys(obj).forEach(key => {
			formData.append(key, obj[key])
		})
		return formData
	}
}

export default new RequestHttp(config)
